package com.hmily.dubbo.consumer.sync;

import com.hmily.dubbo.api.UserQueryFacade;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.socket.SocketChannel;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.Version;
import org.apache.dubbo.remoting.RemotingException;
import org.apache.dubbo.remoting.exchange.Request;
import org.apache.dubbo.remoting.transport.netty4.NettyClientHandler;
import org.apache.dubbo.remoting.transport.netty4.NettyCodecAdapter;
import org.apache.dubbo.remoting.transport.netty4.NettyEventLoopFactory;
import org.apache.dubbo.rpc.RpcInvocation;
import org.apache.dubbo.rpc.protocol.dubbo.DubboCountCodec;

import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.util.HashMap;

import static org.apache.dubbo.remoting.Constants.DEFAULT_CONNECT_TIMEOUT;
import static org.apache.dubbo.remoting.transport.netty4.NettyEventLoopFactory.socketChannelClass;

/**
 * <h1>模拟 Dubbo 最核心的怎么发送数据的。</h1>
 *
 * @author hmilyylimh
 *         ^_^
 * @version 0.0.1
 *         ^_^
 * @date 2022-04-09
 *
 */
public class JavaCodeNettyConsumerApplication
        implements org.apache.dubbo.remoting.ChannelHandler {

    public static void main(String[] args) throws Throwable {
        new JavaCodeNettyConsumerApplication().doStart();
    }

    // NettyClient
    public void doStart() throws Throwable {
        URL url = new URL("", "", 0);
        final NettyClientHandler nettyClientHandler = new NettyClientHandler(url, this);
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(NettyEventLoopFactory.eventLoopGroup(1, "boss"))
                .option(ChannelOption.SO_KEEPALIVE, true)
                .option(ChannelOption.TCP_NODELAY, true)
                .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                .channel(socketChannelClass());
        bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS,
                Math.max(DEFAULT_CONNECT_TIMEOUT, 30000));
        bootstrap.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                NettyCodecAdapter adapter = new NettyCodecAdapter(new DubboCountCodec(), url,
                        JavaCodeNettyConsumerApplication.this);
                ch.pipeline()
                        .addLast("decoder", adapter.getDecoder())
                        .addLast("encoder", adapter.getEncoder())
                        .addLast("handler", nettyClientHandler);
            }
        });
        InetSocketAddress net = new InetSocketAddress("192.168.43.183", 20880);
        ChannelFuture future = bootstrap.connect(net);
        // 这里睡眠一下，主要是等待 connect 彻底连接上后，才可以继续发送数据
        Thread.sleep(1000);
        Channel newChannel = future.channel();
        // 发送数据
        newChannel.writeAndFlush(buildReq());
        System.in.read();
    }

    @Override
    public void connected(org.apache.dubbo.remoting.Channel channel)
            throws RemotingException {
        System.out.println("connected");
    }
    @Override
    public void disconnected(org.apache.dubbo.remoting.Channel channel)
            throws RemotingException {
        System.out.println("disconnected");
    }
    @Override
    public void sent(org.apache.dubbo.remoting.Channel channel, Object message)
            throws RemotingException {
        System.out.println("sent");
    }
    @Override
    public void received(org.apache.dubbo.remoting.Channel channel, Object message)
            throws RemotingException {
        System.out.println("received");
    }
    @Override
    public void caught(org.apache.dubbo.remoting.Channel channel, Throwable exception)
            throws RemotingException {
        System.out.println("caught");
    }

    private static Request buildReq() throws NoSuchMethodException {
        Method method = UserQueryFacade.class.getDeclaredMethod("queryUser", String.class);
        RpcInvocation rpcInvocation = new RpcInvocation(
                method,
                UserQueryFacade.class.getName(),
                null,
                new Object[]{"hmily"});

        HashMap map = new HashMap();
        map.put("path", UserQueryFacade.class.getName());
        rpcInvocation.setAttachments(map);

        //发送消息到服务端
        Request req = new Request();
        req.setVersion(Version.getProtocolVersion());
        req.setTwoWay(true);
        req.setData(rpcInvocation);
        return req;
    }
}